/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    Textbook
 * @subpackage General
 * @author     Aljaz Srebrnič <a2piratesoft@gmail.com>
 * @author     E. Pavletič
 * @copyright  2008 a2PirateSoftware
 * @license    GPL License v3 <http://www.gnu.org/copyleft/gpl.html>
 */

/**
 * Constructor razreda General.
 *
 * Naloga razreda je, da priredi povezavam v meniju ustrezne metode, ki se bodo izvajale ob klikanju miške nanje
 *
 * @class
 * @scope public
 */
function General() {
    var oThis = this;

    // Pripravi metodo za upravljanje nepredvidenih napak na strežniku
    jQuery('ul#errors').ajaxError(function(event, XMLHttpRequest, ajaxOptions, thrownError) {
	if(jQuery(this).children('li').length == 0) {
	    // Vrne napako nazaj na strežnik, ki jo bo tako lahko sporočil (po e-pošti) skrbniku
	    jQuery.post('/index.php?znfModule=general&znfAction=uncheckedExceptions', {'response' : XMLHttpRequest.responseText});
	    
	    // Vrstni red je pomemben: vrstica mora stati za jQuery.post, ker pred jQuery.post se sproži ajaxSend
            oThis.appendError(generalMessages.unknownServerError);
	    
            // Skrije nalepko "Nalagam ..."
            jQuery("div#loading").hide();
	}
	
	event.stopPropagation();
	return false;
    });

    // Premakne ime ('name') skritega polja z identifikatorjem id="loginKey" v atribut 'value' in kopira vrednost atributa 'id' v atribut 'name'
    // Polje služi zato, da se onemogoči avtomatske prijave s strani t.i. 'bot'-ov. Postopek je naslednji:
    //   1) strežnik generira ključ, ki ga pošlje odjemalcu/pregledovalniku. Ključ je sestavljen iz trenutnega
    //      TIMESTAMP in identifikatorja seje (SESSION). Strežnik pošlje ključ odjemalcu dvakrat in sicer:
    //      a) v obliki cookie-ja. V tem primeru ga bo spremenil tako, da bo dvakrat izračunal njegov md5 - md5(md5(ključ))
    //      b) v obliki skritega polja v obrazcu za registracijo. V tem slučaju ga bo spremenil z enim samim
    //         izračunom md5 - md5(ključ). Tako spremenjeno vrednost ključa bo poslal odjemalcu v atributu 'name'
    //         skritega polja z id="loginKey" (<input type="hidden" id="loginKey" name="ključ" value="" />). Na odjemalčevi
    //         strani bo koda v javascriptu premaknila vrednost iz atributa 'name' v atribut 'value' in istočasno
    //         kopirala vrednost identifikatorja skritega polja (id="loginKey") v atribut 'name'.
    // Veljavnost (rok trajanja) cookie-ja določa konstanta razreda COOKIE_EXPIRE_TIME.
    // Ko dobi strežnik podatke obrazca za registracijo najprej poišče vrednost polja z identifikatorjem id="loginKey", jo
    // spremeni tako, da izračuna njen md5 in končno jo primerja z vrednostjo cookie-ja. V primeru, da sta vrednosti
    // enaki, zaključi postopek, sicer vrne napako
    jQuery('div.content div#login form#loginForm input#loginKey').val(jQuery('div.content div#login form#loginForm input#loginKey').attr('name'));
    jQuery('div.content div#login form#loginForm input#loginKey').attr('name', jQuery('div.content div#login form#loginForm input#loginKey').attr('id'));

    /*********************************************************************************/
    /******                            Modul Uporabnik                          ******/
    /*********************************************************************************/
    // Priredi povezavi za uporabnike v meniju metodo, ki se bo izvajala ob klikanju miške
    jQuery('div#menu ul li a#user').click(function() {return oThis.userAction(this);});

    // =================================== Skrbnik ==================================
    // Priredi povezavi za uporabnike v meniju metodo, ki se bo izvajala ob klikanju miške
    jQuery('div#menu div#admin a#userList').click(function() {return oThis.userAction(this);});
    // =============================== Konec skrbnik ===============================
    /*********************************************************************************/
    /******                        Konec modula Uporabnik                       ******/
    /*********************************************************************************/


    /*********************************************************************************/
    /******                             Modul Učbenik                           ******/
    /*********************************************************************************/
    // Priredi povezavi za učbenike v meniju metodo, ki se bo izvajala ob klikanju miške na elemente uporabnika
    jQuery('div#menu ul li:lt(3) a').click(function() {return oThis.textbookAction(this);});
    /*********************************************************************************/
    /******                         Konec modula Učbenik                        ******/
    /*********************************************************************************/

    // Pripne 'ajaxform' (glej JQuery.form) obrazcu za prijavo uporabnika
    // Nastavitve za jQuery.form
    var options = {
        beforeSubmit:  function() {return oThis.checkLoginFormData('div#menu');},           // pre-submit callback: preveri podatke, ki jih je vnesel uporabnik
        resetForm:     true,                                                                // 'resetiranje' obrazca ni avtomatsko ampak ročno (ko se postopek zaključi)
        type:          'post',                                                              // 'get' or 'post', override for form's 'method' attribute
        success:       function(data) {oThis.loginResponse(data);},                         // post-submit callback
	dataType:      'xml'                                                                // 'xml', 'script', or 'json' (pričakovani tip odgovora s strani strežnika)
    };
    // Pripne callback
    jQuery('div#menu div#login form#loginForm').ajaxForm(options);

    // Priredi povezavi za odjavo uporabnika v meniju metodo, ki se bo izvajala ob klikanju miške
    jQuery('div#menu div#logout a').click(function() {return oThis.logoutAction(this);});

    // Priredi povezavi za 'pozabljeno geslo' v meniju metodo, ki se bo izvajala ob klikanju miške
    jQuery('div#menu div#login a#passwordLost').click(function() {return oThis.passwordLost(this);});

    // Auto-prikaz div#loading-a (test)
    jQuery('div#loading').ajaxSend(function() {jQuery(this).show();} ).ajaxStop(function() {jQuery(this).hide();} );

    // Izprazne blok <div id="errors"> ob pred vsako izvedbo XMLHttpRequest
    jQuery('div#errorDialog').ajaxSend(function() {
	oThis.clearError();
    });

    // Metoda ajaxSuccess služi za Google analytics. Ob vsaki uspešni zahtevi XHR nastavi URL,
    // ki ga bo uporabila koda za google analytics. Upoštevane so tudi strani ki vrnejo napake (errors).
    jQuery('body').ajaxSuccess (function(event, XMLHttpRequest, ajaxOptions) {
        if (ajaxOptions.dataType == 'xml') {
            var regexUrlModule = /^[\w\.\/]*\?\S*znfModule=(\w+)/;
            var regexUrlAction = /^[\w\.\/]*\?\S*znfAction=(\w+)/;
            var urlModule = ajaxOptions.url.match(regexUrlModule);
            var urlAction = ajaxOptions.url.match(regexUrlAction);

            pageTracker._trackPageview('/'+urlModule[1]+'/'+urlAction[1]);
        }
    });
}

General.prototype.clearError = function(){
    jQuery('div#errorDialog').hide();
    jQuery('ul#errors').empty();
}

General.prototype.appendError = function(message){
    jQuery('ul#errors').append("<li>" + message + "</li>");
    jQuery('div#errorDialog').show();
}


// Atribut, ki vsebuje css značke, ki je povzročila zadnji dogodek (event). Ko namreč uporabnik klika
// na povezavo zaščitene strani, se shrani css značke v ta atribut. Po uspeščem overjanju program simulira
// (trigger) klik miške na isto značko, tako da se naloži zahtevana stran. V ostalih primerih atribut MORA vsebovati vrednost NULL.
General.prototype.target = null;


// Atribut, ki vsebuje objekt za upravljanje uporabnikov (registracijo, prijave, ...)
General.prototype.user = null;


// Atribut, ki vsebuje objekt za upravljanje učbenikov (iskanje, vnos, spreminjanje, ...)
General.prototype.textbook = null;


/**
 * Metoda userAction
 *
 * Metoda ustvari istanco objekta User v primeru, da ne še obstaja, in mu prepusti upravljanje strani
 * @scope public
 */
General.prototype.userAction = function(oDom) {
    if (this.user == null)
	this.user = new User();

    return this.user.doAction(oDom);
}


/**
 * Metoda textbookAction
 *
 * Metoda ustvari istanco objekta Textbook v primeru, da ne še obstaja, in mu prepusti upravljanje strani
 * @scope public
 */
General.prototype.textbookAction = function(oDom) {
    if (this.textbook == null)
	this.textbook = new Textbook();

    this.textbook.doAction(oDom);

    return false;
}


/**
 * Metoda unescapeHTML
 *
 * Metoda zamenja vse znake, ki so bili kodirani s PHPjevo funkcijo htmlspecialchars, z znaki HTMLja
 * @scope protected
 */
General.prototype.unescapeHTML = function(s) {
    return s.replace(/&(amp|[lg]t|quot);/g,
		     function(m, p1) {
			 var map = {
			     amp:  "&",
			     lt:   "<",
			     gt:   ">",
			     quot: '"'
			 };
			 return map[p1];
		     });
}

/**
 * Metoda preveri podatke, ki jih vrne strežnik. Možnosti so:
 * 1) strežnik je vrnil napako: v tem primeru metoda prikaže napako in se zaključi
 * 2) strežnik zahteva overjanje (zahtevana stran je zaščitena): v tem primeru metoda prikaže obrazec za overjanje in se zaključi
 * 3) strežnik je vrnil zahtevano stran: metoda ne naredi nič in prepusti upravljanje naložene strani tisti metodi, ki jo je klicala
 *
 * @param data XML objekt. Odgovor strežnika
 * @param target Objekt DOM. Objekt DOM na katerem se je sprožila zahteva. Lahko je tudi odsoten (v tem primeru bo undefined)
 */
General.prototype.checkResponse = function(data, target) {
    var oThis = this;

    // Izpiše morebitne napake
    if (jQuery(data).find('errors').length > 0) {
	jQuery(data).find('errors error').each(function () {
            oThis.appendError(jQuery(this).text());
	});
        return false;
    }

    // Preveri če je stran zaščitena (v odgovoru je prisotna značka <login />). V tem primeru prikaže obrazec za overjanje in vrne 'false'.
    if (jQuery(data).find('login').length > 0) {
	// Vnese obrazec za overjanje (kodo html) v blok z identifikatorjem "content"
	jQuery('div#page div#content').empty().html("<h3>" + generalMessages.authRequired + "</h3>");
	var cloneLogin = jQuery('div#menu div#login').clone().removeClass('small').show();
	cloneLogin.attr('id', 'loginClone');
	cloneLogin.find('form#loginForm').attr('id', 'loginFormClone');
	cloneLogin.find('input#loginKey').attr('id', 'loginKeyClone');
	cloneLogin.find('input#username').attr('id', 'usernameClone');
	cloneLogin.find('input#password').attr('id', 'passwordClone');
	cloneLogin.find('a#passwordLost').attr('id', 'passwordLostClone');
	cloneLogin.find('button#submitButton').attr('id', 'submitButtonClone');
	jQuery('div#page div#content').append(cloneLogin);

	// Priredi povezavi za 'pozabljeno geslo' v meniju metodo, ki se bo izvajala ob klikanju miške
	jQuery('div#page div#content div#loginClone a#passwordLostClone').click(function() {return oThis.passwordLost(this);});

	// Pripne 'ajaxform' (glej JQuery.form) obrazcu za prijavo uporabnika
	// Nastavitve za jQuery.form
	var options = {
            beforeSubmit:  function() {return oThis.checkLoginFormData('div#page', 'Clone');},    // pre-submit callback: preveri podatke, ki jih je vnesel uporabnik
            resetForm:     true,                                                         // 'resetiranje' obrazca ni avtomatsko ampak ročno (ko se postopek zaključi)
            type:          'post',                                                       // 'get' or 'post', override for form's 'method' attribute
            success:       function(data) {oThis.loginResponse(data);},                  // post-submit callback
	    dataType:      'xml'                                                         // 'xml', 'script', or 'json' (pričakovani tip odgovora s strani strežnika)
	};
	// Pripne callback
	jQuery('div#page div#content div#loginClone form#loginFormClone').ajaxForm(options);

	// Skrije obrazec za overjanje v stranskem predelu (meniju)
	jQuery('div#menu div#login').hide();

	// Shrani CSS naslov značke, ki je generirala dogodek, seveda če je definiran. V primeru da določena stran ni zaščitena
	//(ne zahteva overjenja) podatek ni potreben, ker koda v bloku login se ne bo izvedla
	if (typeof target !== 'undefined')
	    this.target = target;

        return false;
    }

    // Preveri če je uporabnik tudi skrbnik. V tem primeru prikaže meni za administracijo
    if (jQuery(data).find('login-roles role[name="admin"]').length > 0) {
	jQuery('div#menu div#admin').show();
    }

    return true;
}


/**
 * Metoda checkLoginFormData
 *
 * Metoda preveri podatke v obrazcu preden jih pošlje strežniku. Polja, ki jih metoda preveri in ki so tudi obvezna, so:
 * <ul>
 *   <li><b>username</b> - Polje za uporabniško ime. Preveri prisotnost</li>
 *   <li><b>password</b> - Polje za geslo</li>
 * </ul>
 * V primeru napak jih izpiše v ustrezno področje (div#errors) in ustavi postopek.
 *
 * @param area Področje (css), kjer se nahaja obrazec za prijavo (v meniju ali na glavni strani)
 * @param postfix Vrednost parametra je 'Clone' v primeru, da se obrazec za prijavo nahaja na glavni strani, sicer je 'undefined'
 * @scope private
 */
General.prototype.checkLoginFormData = function(area, postfix) {
    var errors = new Array();
    var i = 0;

    // Če parameter ni definiran, pomeni, da uporabljamo obrazec v meniju
    if (typeof(postfix)=='undefined') 
	postfix = '';

    // Najprej skrije področje za napake in ga izprazne (izbriše stare napake)
    this.clearError();

    // uporabniško ime
    if (jQuery.trim(jQuery(area+' div#login'+postfix+' form#loginForm'+postfix+' input#username'+postfix).val()).length == 0) {
        errors[i] = userMessages.emptyUsername;
        ++i;
    }

    // geslo
    if (jQuery.trim(jQuery(area+' div#login'+postfix+' form#loginForm'+postfix+' input#password'+postfix).val()).length == 0) {
        errors[i] = userMessages.emptyPassword;
        ++i;
    }

    // Preveri če so v obrazcu prisotne napake. V tem primeru jih prikaže in postopek zaključi
    if (errors.length > 0) {
	for (var i=0; i < errors.length; ++i)
            this.appendError(errors[i]);
	
	jQuery('div#errorDialog').show();
	return false;
    }

    return true;
}


/**
 * Metoda preveri odgovor strežnika in:
 * 1) v primeru napak, jih prikaže
 * 2) sicer postavi novo kodo html v blok z identifikatorjem 'content' (id="content")
 * NB: Metoda je uporabljiva takrat, ko kodi, ki bo prikazana, ni potrebno dodajati callbackov
 *
 * @param data Dokument xml, ki ga je vrnil strežnik
 * @scope private
 */
General.prototype.showResponse = function(data) {
    if (!this.checkResponse(data))
		return false;

    // Ni prišlo do napake. Vnese html kodo v blok z identifikatorjem "content"
    jQuery('div#page div#content').empty();
    jQuery('div#page div#content').html(this.unescapeHTML(jQuery(data).find('content html').text()));

    return true;
}


/**
 * Metoda preveri odgovor strežnika in:
 * 1) v primeru napak, jih prikaže
 * 2) sicer skrije obrazec za prijavo in prikaže gumb za odjavo
 *
 * @param data Dokument xml, ki ga je vrnil strežnik
 * @scope private
 */
General.prototype.loginResponse = function(data) {
    if (!this.checkResponse(data))
	return;

    // Ni prišlo do napake. Skrije obrazec za prijavo in prikaže gumb za odjavo
    jQuery('div#menu div#login').hide();
    jQuery('div#menu div#logout').show();

    // Preveri če mora naložiti novo stran (v content). V tem primeru zaključi izvajanje, ne skrije nalepke 'Nalagam ...'
    // Uporabnik je namreč lahko zahteval zaščiteno stran in v tem primeru se je avtomatsko izvedel postopet prijave (login).
    // Po uspešni prijavi pa mora aplikacija prikazati zahtevano stran.
    if (this.target !== null) {
	// Sproži dogodek 'click' na izbranem elementu/značli
	jQuery(this.target).trigger('click');

	// Postavi vrednost atributa na null
	this.target = null;
	return true;
    }
}


/**
 * Metoda se izvede ko uporabnik klika na gumb za odjavo
 *
 * @param tag Kazalec na značko/povezavo, ki je povzročila izvajanje metode
 */
General.prototype.logoutAction = function(tag) {
    var oThis = this;
    var url = jQuery(tag).attr('href');

    // Izvede zahtevo na strežniku
    jQuery.get(url, null, function(data) {
	// Preveri odgovor strežnika in v primeru napak, jih prikaže ter zaključi izvajanje
	if (!oThis.checkResponse(data))
	    return false;

	// V primeru, da ni bilo napak, preusmeri pregledovalnik za domačo stran
        window.location = 'index.php';
    }, 'xml');

    return false;
}


/**
 * Metoda se izvede ko uporabnik klika na povezavo "Pozabljeno geslo"
 *
 * @param tag Kazalec na značko/povezavo, ki je povzročila izvajanje metode
 */
General.prototype.passwordLost = function(tag) {
    var oThis = this;
    var url = jQuery(tag).attr('href');

    // Izvede zahtevo na strežniku
    jQuery.get(url, null, function(data) {
	    if (!oThis.checkResponse(data))
	    return false;

	// Ni prišlo do napake. Vnese html kodo v blok z identifikatorjem "content"
	jQuery('div#page div#content').empty();
	jQuery('div#page div#content').html(oThis.unescapeHTML(jQuery(data).find('content html').text())).ready(function() {
	    // Premakne ime ('name') skritega polja z identifikatorjem id="key" v atribut 'value' in kopira vrednost atrinuta 'id' v atribut 'name'
	    // Polje služi zato, da se onemogoči avtomatske prijave s strani t.i. 'bot'-ov. Postopek je naslednji:
	    //   1) strežnik generira ključ, ki ga pošlje odjemalcu/pregledovalniku. Ključ je sestavljen iz trenutnega
	    //      TIMESTAMP in identifikatorja seje (SESSION). Strežnik pošlje ključ odjemalcu dvakrat in sicer:
	    //      a) v obliki cookie-ja. V tem primeru ga bo spremenil tako, da bo dvakrat izračunal njegov md5 - md5(md5(ključ))
	    //      b) v obliki skritega polja v obrazcu za registracijo. V tem slučaju ga bo spremenil z enim samim
	    //         izračunom md5 - md5(ključ). Tako spremenjeno vrednost ključa bo poslal odjemalcu v atributu 'name'
	    //         skritega polja z id="key" (<input type="hidden" id="key" name="ključ" value="" />). Na odjemalčevi
	    //         strani bo koda v javascriptu premaknila vrednost iz atributa 'name' v atribut 'value' in istočasno
	    //         kopirala vrednost identifikatorja skritega polja (id="key") v atribut 'name'.
	    // Veljavnost (rok trajanja) cookie-ja določa konstanta razreda COOKIE_EXPIRE_TIME.
	    // Ko dobi strežnik podatke obrazca za registracijo najprej poišče vrednost polja z identifikatorjem id="key", jo
	    // spremeni tako, da izračuna njen md5 in končno jo primerja z vrednostjo cookie-ja. V primeru, da sta vrednosti
	    // enaki, zaključi postopek, sicer vrne napako
	    jQuery('div#page div#content form#lostPasswordForm input#key').val(jQuery('div#page div#content form#lostPasswordForm input#key').attr('name'));
	    jQuery('div#page div#content form#lostPasswordForm input#key').attr('name', jQuery('div#page div#content form#lostPasswordForm input#key').attr('id'));

	    // Pripne 'ajaxform' (glej JQuery.form) obrazcu za pridobitev novega gesla
            // Nastavitve za jQuery.form
            var options = {
		beforeSubmit:  function() {oThis.checkForgotPasswordFormData;},                                   // pre-submit callback: preveri podatke, ki jih je vnesel uporabnik
		resetForm:     false,                                                               // 'resetiranje' obrazca ni avtomatsko ampak ročno (ko se postopek zaključi)
		type:          'post',                                                              // 'get' or 'post', override for form's 'method' attribute
		success:       function(data) {oThis.showResponse(data);},  // post-submit callback
		dataType:      'xml'                                                                // 'xml', 'script', or 'json' (pričakovani tip odgovora s strani strežnika)
            };
	    // Pripne callback
            jQuery('div#page div#content form#lostPasswordForm').ajaxForm(options);
	});
    }, 'xml');

    return false;
}


/**
 * Metoda checkForgotPasswordFormData
 *
 * Metoda preveri podatke v obrazcu za pozabljeno geslo preden jih pošlje strežniku. Polja, ki jih metoda preveri in ki so tudi obvezna, so:
 * <ul>
 *   <li><b>username</b> - Polje za uporabniško ime. Preveri prisotnost</li>
 *   <li><b>email</b> - Polje za naslovelektronske pošte</li>
 * </ul>
 * V primeru napak jih izpiše v ustrezno področje (div#errors) in prekine postopek.
 *
 * @scope private
 */
General.prototype.checkForgotPasswordFormData = function(area) {
    var errors = new Array();
    var i = 0;

    // Najprej skrije področje za napake in ga izprazne (izbriše stare napake)
    this.clearError();

    // uporabniško ime
    if (jQuery.trim(jQuery('div#content form#lostPasswordForm input#username').val()).length == 0) {
        errors[i] = userMessages.emptyUsername;
        ++i;
    }

    // naslov elektronske pošte
    if (jQuery.trim(jQuery('div#content form#lostPasswordForm input#email').val()).length == 0) {
        errors[i] = userMessages.emptyEmail;
        ++i;
    }

    // Preveri če so v obrazcu prisotne napake. V tem primeru jih prikaže in postopek zaključi
    if (errors.length > 0) {
	for (var i=0; i < errors.length; ++i)
            this.appendError(errors[i]);
	
	jQuery('div#errorDialog').show();
	return false;
    }

    return true;
}


/*******************************************************************************************************
 ***                Ustvari istanco razreda General (ko se zaključi nalaganje strani)                ***
 ***                                         NE PRIPADA RAZREDU                                      ***
 *******************************************************************************************************/
jQuery(document).ready(function() {
    var general = new General();
});
